//Copyright  2015 Max "Shrinker" Wieden
//Refer to license for license details.

//Grammar of the Iteration 10 Programming Language (IXPL)

Tokens:
	Include           = '#' ([' ', '\t'])* "include" ([' ', '\t'])* '\"' (~['\"'])+ '\"';
	Name              = ['@'] FirstNameChar (FirstNameChar | Digit)*; //@ is ignored, but allows keywords to be used as names
	FloatLiteral      = (BaseFloatLiteral [FloatExponent] | NumericLiteral FloatExponent) ('F' | 'f');
	DoubleLiteral     = (BaseFloatLiteral [FloatExponent] | NumericLiteral FloatExponent) ('D' | 'd');
	#FloatExponent    = ['E', 'e'] [['+', '-']] (Digit)+;
	LongLiteral       = (HexLiteral | NumericLiteral) ('L' | 'l');
	IntLiteral        = HexLiteral | NumericLiteral;
	#CharContent      = '\\' ['r', 'n', 't', '\'', '\"', '\\'] | ~['\'', '\"', '\\'];
	CharLiteral       = '\''  CharContent   '\'';
	StringLiteral     =
		'\"' (CharContent)* '\"' |
		'\\' //"abc" or (\""abc"" or \"#abc#" or \"(abc)" or ...)
		(
			"\"[" (~[']'])* (']')+ (~[']', '\"'] (~[']'])* (']')+)* '\"'
		|	"\"{" (~['}'])* ('}')+ (~['}', '\"'] (~['}'])* ('}')+)* '\"'
		|	"\"<" (~['>'])* ('>')+ (~['>', '\"'] (~['>'])* ('>')+)* '\"'
		|	"\"(" (~[')'])* (')')+ (~[')', '\"'] (~[')'])* (')')+)* '\"'
		|	'\"' \strWithCustomDelims{'\"'}
		);
	#FirstNameChar    = ['A'-'Z'] | ['a'-'z'] | ['_', '$'];
	#Digit            = ['0'-'9'];
	#HexDigit         = Digit | ['A'-'F'] | ['a'-'f'];
	#NumericLiteral   = '0' | ['1'-'9'] (Digit)*;
	#HexLiteral       = '0' ['X', 'x'] (HexDigit)+;
	#BaseFloatLiteral = (Digit)+ '.' (Digit)* | '.' (Digit)+;
	AssignPower       = "**=";
	AssignShiftL      = "<<=";
	AssignShiftR      = ">>="; //unsigned right shift
	AssignAdd         = "+=" ;
	AssignAt          = "@=" ;
	AssignDiv         = "/=" ;
	AssignIntAnd      = "&=" ;
	AssignIntOr       = "|=" ;
	AssignIntXOR      = "^=" ;
	AssignMod         = "%=" ;
	AssignMul         = "*=" ;
	AssignSub         = "-=" ;
	DoubleAnd         = "&&" ;
	DoubleCaret       = "^^" ;
	DoubleMinus       = "--" ;
	DoublePipe        = "||" ;
	DoublePlus        = "++" ;
	Equals            = "==" ;
	GreaterOrEqual    = ">=" ;
	LessOrEqual       = "<=" ;
	NotEquals         = "!=" ;
	Power             = "**" ;
	Scope             = "::" ;
	ShiftL            = "<<" ;
	ShiftR            = ">>" ; //unsigned right shift
	And               = '&'  ;
	Assign            = '='  ;
	Asterisk          = '*'  ;
	Caret             = '^'  ;
	Colon             = ':'  ;
	Comma             = ','  ;
	Exclamation       = '!'  ;
	Greater           = '>'  ;
	LBrace            = '{'  ;
	LBracket          = '['  ;
	Less              = '<'  ;
	LPar              = '('  ;
	Minus             = '-'  ;
	Percent           = '%'  ;
	Period            = '.'  ;
	Pipe              = '|'  ;
	Plus              = '+'  ;
	QuestionMark      = '?'  ;
	RBrace            = '}'  ;
	RBracket          = ']'  ;
	RPar              = ')'  ;
	Semicolon         = ';'  ;
	Slash             = '/'  ;
	Tilde             = '~'  ;

FillerTokens:
	SingleLineComment = "//" (~['\r', '\n'])* ["\r\n" | ['\r', '\n']];
	MultiLineComment  = "/*" (~['*'])* ('*')+ (~['*', '/'] (~['*'])* ('*')+)* '/';
	Whitespace        = (['\t', ' ', '\r', '\n'])+;

TokenSpecializations:
	//keywords
	KeyType      = Name("type"     );
	KeyNamespace = Name("namespace");
	KeyUsing     = Name("using"    );
	KeyIf        = Name("if"       );
	KeyElse      = Name("else"     );
	KeyReturn    = Name("return"   );
	KeyWhile     = Name("while"    );
	KeyDo        = Name("do"       );
	KeyFor       = Name("for"      );
	KeyContinue  = Name("continue" );
	KeyBreak     = Name("break"    );
	KeyInterrupt = Name("interrupt");
	KeyInterpret = Name("interpret");
	KeyStatic    = Name("static"   );
	//types
	KeyVoid      = Name("void"     );
	KeyChar      = Name("char"     ); //semantics: those of KeyInt
	KeyInt       = Name("int"      );
	KeyLong      = Name("long"     );
	KeyFloat     = Name("float"    );
	KeyDouble    = Name("double"   );
	KeyVec3      = Name("vec3"     );
	KeyFunction  = Name("function" );
	KeyTuple     = Name("tuple"    );
	KeyString    = Name("string"   );
	//literals
	KeyFalse     = Name("false"    );
	KeyTrue      = Name("true"     );

Rules:
//
// global stuff
//
Start = {
	//error message if a file could not be found: Can't find file "<path>" in local directory or configured include directories.
	//the list is searched from start to end; so the first entries have the priority
	(IncludeStatement | GlobalStatement)*
	EOF
}

IncludeStatement = {
	//1. determine absolute target file path
	//1.1 if it is already an absolute path, try it
	//1.2 (first alternative) query local directory, ignore a match if the target file is the input file (an include file with the same name is preferred in this particular case, assuming that nobody would want to include a file in itself, which would have no effect anyway)
	//1.3 (second alternative) query all configured include directories
	//2. if file hasn't already been processed, parse it with a new parser instance and process it, in the current environment with the currently known symbols
	//the directory separator char of choice is the forward slash
	//error message if a file could not be found (pointing at Include token): Can't find include file "<path>" in local directory or configured include directories.
	Include
}

GlobalStatement = {
	//there are a global scope and local scopes
	//namespaces are scopes too, but they bear names
	//a namespace can have unnamed scopes and namespaces as children
	//an unnamed scope can only have unnamed child scopes
	//a scope contains symbols and other scopes
	//names within a scope must be unique between used or declared symbols and between used or declared namespaces, therefore, a symbol and a namespace may bear the same name
	//a symbol can be a function group, a type, or a variable
	//functions inside of namespaces may be removed by the optimization stage when they are not used; global functions are be retained so they can be called by the host program
	Lookahead(KeyNamespace QNameDef LBrace) NamespaceDefinition
|	Lookahead(KeyNamespace QNameDef Assign QName KeyVoid) NamespaceNameChange
|	NamespaceAlias
|	NamespaceUsage
|	Lookahead(FunctionDeclaration) FunctionDeclaration
|	Lookahead(FunctionReturnType Name LPar) FunctionDefinition
|	StaticBlock
|	TypeDefinition
|	Semicolon //empty statement
|	VariableDefinition Semicolon
}

//
// type symbols
//
TypeDefinition = {
	//a new type is added to the current scope only after it has been defined, disallowing a type to be defined with itself
	//error message if a symbol with that name already existed and was a type (pointing at Name token): Can't redefine type "<name>" in current scope.
	//error message if a symbol with that name already existed and was not a type (pointing at Name token): Can't redefine "<name>" as type in current scope.
	KeyType Name Assign Type (Comma Name Assign Type)*
}

Type = {
	//contents of a type definition
	//two types are considered equal if all types in the tree of this body are equal, breaking them down to the atomic types and ignoring the element names
	//error message if a QName was used that does not denote a type (pointing at last Name token in QName): Referenced symbol "<name>" must be a type.
	(
		BaseType
	|	KeyString //= int[]
	|	TupleType
	|	FunctionType
	|	QName //reference to a type symbol, for copying the definition
	)
	(Lookahead(TypeArrayModifier)
		//creates an array similar to an std::vector<T> in C++, a System.Collections.Generic.List<T> in C#, or a java.util.ArrayList<T> in Java
		//an array here is dynamic and can therefore change in size
		//And present => reference flag set for element type
		//the value of an expression yielding a mutable type can be reassigned
		//the target location of an expression yielding a mutable type with the reference flag set can be reassigned
		TypeArrayModifier
	)*
}

TypeArrayModifier = {
	[And] LBracket RBracket
}

BaseType = {
	//32 or 64 bit signed int (64 bit: long) or IEEE-754 32 or 64 bit signed float (64 bit: double) or an atomic triple of floats
	//long literals are suffixed with an 'l' (or 'L')
	//int and long literals can be expressed in hexadecimal notation 0xFF (with the symbols 'A'-'F', 'a'-'f')
	//float literals have a period in them and are suffixed with an 'f' (or 'F') like in similar languages
	//double literals are suffixed with a 'd' (or 'D')
	KeyChar | KeyInt | KeyLong | KeyFloat | KeyDouble | KeyVec3
}

TupleType = {
	//the individual elements of a tuple type may be named
	//the names must be unique only compared to other names in the same tuple
	//And present => reference flag set for that element's type
	//error message if a tuple element name was used more than once (pointing at Name token): Can't use element name "<name>" more than once in the same Tuple.
	KeyTuple LPar Type [And] [Name] (Comma Type [And] [Name])* RPar
|	LPar Type [And] [Name] (Comma Type [And] [Name])+ RPar
}

FunctionType = {
	//a variable of this function type can hold a reference to a function, and can be called
	//the parameter types must match, the parameter reference flags may differ; they are taken into account when evaluating the arguments
	//when the return value of a function object type call is accessed, the tuple element naming of the function object's return type applies
	KeyFunction Less FullFunctionTypeSignature Greater
}

FullFunctionTypeSignature = {
	FunctionReturnType FunctionDeclarationParameters
}

FunctionReturnType = {
	//an And token indicates a reference returned, to be used with reference variables
	KeyVoid | Type [And]
}

//
// static blocks
//
StaticBlock = {
	//is executed with the global variable initialization, there may be multiple static blocks
	//if a file is included more than once, the statics in it are only executed once because subsequent includes are ignored
	//returns in a static block jump to the end of it
	KeyStatic StatementBlock //enclosed in a new scope
}

//
// variable symbols
//
VariableDefinition = {
	//see also: FunctionDefinition
	//see also: FunctionDefinitionParameter
	//a new variable is added to the current scope only after it has been defined or there was a terminator, disallowing a variable to be defined with itself
	//the AssignmentExpression types must match the variable type unless an implicit cast is applicable; the reference flags are ignored:
	// int    variable and long/float/double AssignmentExpression
	// long   variable and int /float/double AssignmentExpression
	// float  variable and int /long /double AssignmentExpression
	// double variable and int /long /float  AssignmentExpression
	//if the And token is present to indicate a reference variable, implicit casting is not used
	//error message if a symbol with that name already existed and was a variable (pointing at Name token): Can't redefine variable "<name>" in current scope.
	//error message if a symbol with that name already existed and was not a variable (pointing at Name token): Can't redefine "<name>" as variable in current scope.
	//error message if an optional AssignmentExpression's type is not compatible (pointing at Assign token): Can't assign variable from given expression.
	Type [And] Name [Assign AssignmentExpression] (Comma [And] Name [Assign AssignmentExpression])*
}

//
// function group symbols
//
FunctionDeclaration = {
	//see also: FunctionDefinition
	//if there already exists a function group of that name, this new function declaration is added, otherwise a new function group is created for this declaration
	//the function is added only after the signature has been evaluated completely, disallowing the signature to be defined using itself
	//overloading of an already existing function is allowed if the parameter count or at least one parameter type differs, parameter reference flags and parameter names are ignored when checking for this
	//before a declared function is defined, the tuple element names from the declaration's return type are used; after it has been defined, the tuple element names from the return type of the definition are used
	//a function must not be declared if there already is such a function group and that one can't be overloaded with this function
	//if a namespace is used as a whole, newly introduced functions may bear the same signatures (violating the overloading conditions), but they are used only when no original function was found
	//error message if a symbol with that name already existed and was not a function group (pointing at Name token): Can't redeclare "<name>" as function in current scope.
	//error message if a symbol with that name already existed and was a function group and could not be overloaded (pointing at Name token): Can't overload function "<name>" with given parameter list in current scope.
	FunctionReturnType Name FunctionDeclarationParameters Semicolon
}

FunctionDeclarationParameters = {
	//names are ignored
	LPar [FunctionDeclarationParameter (Comma FunctionDeclarationParameter)*] RPar
}

FunctionDeclarationParameter = {
	//And indicates a reference parameter
	//when a reference parameter is initialized with a variable, that variable can be altered directly
	//if it is instead initialized with a normal value not stored in a variable, reassigning it has no side-effects as the value will be dropped afterwards
	//the name is ignored
	Type [And] [Name]
}

FunctionDefinition = {
	//see also: FunctionDeclaration
	//the function is added only after the signature has been evaluated completely, but before the body is evaluated, disallowing the signature to be defined using itself but allowing it to call itself
	//the overloading condition may be violated only if the already existing function has not been defined yet, but only declared
	//the parameter names must be unique within the function signature
	//error message if a symbol with that name already existed and was not a function group (pointing at Name token): Can't redefine "<name>" as function in current scope.
	//error message if a symbol with that name already existed and was a function group and could not be overloaded (pointing at Name token): Can't overload function "<name>" with given parameter list in current scope.
	//error message if it a declared match has been found and it is attempted to define it with a different return type or return reference flag (pointing at Name token): Can't define function "<name>" with different return type than the declaration in current scope.
	FunctionReturnType Name LPar [FunctionDefinitionParameter (Comma FunctionDefinitionParameter)*] RPar (StatementBlock | WeakerStatement) //enclosed in a new scope
}

FunctionDefinitionParameter = {
	//see also: FunctionDeclarationParameter
	//see also: VariableDefinition
	//error message if a function parameter name was used more than once (pointing at Name token): Can't use parameter name "<name>" more than once in the same function definition.
	Type [And] Name
}

//
// namespaces
//
QName = {
	//[Scope] at beginning directly denotes the global namespace
	//the lookup of symbols and namespaces of any kind is as follows:
	//1. check the most local scope
	//2. check the used namespaces (but not additionally used namespaces)
	//3. start with 1 again, but for the enclosing parent scope
	//in check of the used namespace children, an error for ambiguity is returned if the symbol exists more than once; function overloadability is not taken into account for this ambiguity warning
	//error message if a symbol wasn't found (pointing at last Name token): Symbol "<name>" not found.
	//error message if a function group wasn't found (pointing at last Name token): Function "<name>" not found.
	//error message if a namespace wasn't found (pointing at last Name token): Namespace "<name>" not found.
	[Scope] Name (Scope Name)*
}

QNameDef = {
	Name (Scope Name)*
}

NamespaceDefinition = {
	//the named namespace is added to, or, as a namespace object which is to be reopened, retrieved from the namespaces of the current namespace scope
	//children of used namespaces can be reopened too, and so can aliased or renamed namespaces as they are treated as regular children
	//a namespace can't be aliased as or renamed to a certain name if there already is a child namespace or child namespace in a used namespace with the same name in the current namespace scope (applies to NamespaceAlias and NamespaceNameChange rules)
	KeyNamespace QNameDef LBrace (GlobalStatement)* RBrace
}

NamespaceAlias = {
	//see also: NamespaceDefinition
	//an alias behaves like the definition of a new namespace
	//aliased namespaces can be reopened in a NamespaceDefinition
	//currently open namespaces may be aliased too
	//error message if the condition mentioned in NamespaceDefinition is violated (pointing at Assign token): Can't redefine already existing child namespace or child namespace of a used namespace, "<left name>", with an alias, but only re-open it.
	KeyNamespace QNameDef Assign QName Semicolon
}

NamespaceNameChange = {
	//see also: NamespaceDefinition
	//a name change behaves like the definition of a new namespace
	//renamed namespaces can be reopened in a NamespaceDefinition
	//when a namespace is renamed, it must be one that is not currently open
	//contents of a renamed namespace are still accessible to the scopes that "used" it, but for qualifying the contents through their parent, the new name must be used
	//error message if the condition mentioned in NamespaceDefinition is violated (pointing at Assign token): Can't redefine already existing child namespace or child namespace of a used namespace, "<left name>", with a name change, but only re-open it.
	//error message if the namespace to be renamed is a currently open namespace (pointing at Assign token): Can't rename namespace "<name>" because it is a currently open namespace.
	KeyNamespace QNameDef Assign QName KeyVoid Semicolon
}

NamespaceUsage = {
	//using an individual symbol from the namespace is like declaring/defining it: function objects must be overloadable, types and variables must not be overriden
	//using a whole namespace means that the invidual symbols of it may be "overwritten" in the current scope, as they are only considered as a lookup alternative if a symbol wasn't found, and they are only followed once
	//a namespace can't be used if it is a currently open namespace, or if it has already been used in the current namespace scope
	//using a namespace is different than using a FunctionObject or a symbol: the contents become visible
	//when a function symbol is used, its return type and parameter types must be specified with the optional part of this rule
	//error message if a namespace is used and that namespace is already open (pointing at last Name token in QName): Can't use namespace "<name>" because it is a currently open namespace.
	//error message if a namespace is used and is already used (pointing at last Name token in QName): Can't use namespace "<name>" because it is already being used in the current namespace.
	//error message if a FunctionObject is used but there's already a non-function symbol with the same name (pointing at last Name token in QName): Can't use function symbol "<name>" because there already is a non-function symbol with the same name.
	//error message if a FunctionObject is used but the already existing function symbols can't be overloaded with it (pointing at last Name token in QName): Can't use function symbol "<name>" because the functions already in the scope can't be overloaded with it.
	//error message if the QName branch is used and it denotes a function group (pointing at last Name token in QName): Can't use function symbol "<name>" without specifying a parameter list.
	//error message if the QName branch is used and it denotes a symbol whose name is alrady in use (pointing at last Name token in QName): Can't use non-function symbol "<name>" because there already is a non-function symbol with the same name.
	KeyUsing
	(
		KeyNamespace QName
	|	Lookahead(QName Less) FunctionObject
	|	QName
	)
	Semicolon
}

//
// statements
//
StatementBlock = {
	//where a StatementBlock is used, there is usually a new scope created
	//that doesn't happen inside of this rule to allow for function parameters and loop variables to count against the new scope
	LBrace
	(Statement)*
	RBrace
}

Statement = {
	//only the read-only namespace tools are available here, none to alter already existing namespaces
	TypeDefinition
|	Lookahead(Type [And] Name [Assign AssignmentExpression] (Comma | Semicolon)) VariableDefinition Semicolon
|	NamespaceUsage
|	StatementBlock //enclosed in a new scope
|	WeakerStatement
}

WeakerStatement = {
	//error message if there is a continue outside of a loop (pointing at KeyContinue token): There is no enclosing loop to continue at this point.
	//error message if there is a break outside of a loop (pointing at KeyBreak token): There is no enclosing loop to break at this point.
	AssignmentExpression Semicolon
|	IfElseStatement
|	WhileLoopStatement
|	DoWhileLoopStatement
|	ForLoopStatement
|	KeyContinue Semicolon
|	KeyBreak Semicolon
|	ReturnStatement
|	InterruptStatement
|	Semicolon //empty statement
}

//
// control structures
//
IfElseStatement = {
	//the AssignmentExpression is the condition and must be of int type; for clarity in code, implicit casting from long/float/double is not used here
	//error message if the condition is not of int type (pointing at LPar token): If-else condition must be of int type.
	KeyIf LPar AssignmentExpression RPar
	(
		StatementBlock //enclosed in a new scope
	|	WeakerStatement
	)
	[
		KeyElse
		(
			StatementBlock //enclosed in a new scope
		|	WeakerStatement
		)
	]
}

WhileLoopStatement = {
	//the AssignmentExpression is the condition and must be of int type; for clarity in code, implicit casting from long/float/double is not used here
	//error message if the condition is not of int type (pointing at LPar token): While-loop condition must be of int type.
	KeyWhile LPar AssignmentExpression RPar
	(
		StatementBlock //enclosed in a new scope
	|	WeakerStatement
	)
}

DoWhileLoopStatement = {
	//the AssignmentExpression is the condition and must be of int type; for clarity in code, implicit casting from long/float/double is not used here
	//error message if the condition is not of int type (pointing at LPar token): Do-while-loop condition must be of int type.
	KeyDo
	(
		StatementBlock //enclosed in a new scope
	|	WeakerStatement
	)
	KeyWhile LPar AssignmentExpression RPar Semicolon
}

ForLoopStatement = {
	//the whole looping statement is enclosed in a new scope
	//variables introduced in the first part live until the loop's scope ends
	//the second part is the condition and must be of int type; for clarity in code, implicit casting from long/float/double is not used here
	//if the second part is omitted, it automatically evaluates to true, making this loop behaves like while (true) {...}
	//the third part is the incremental part, and allows for operating on multiple loop variables at once
	//error message if the condition, if supplied, is not of int type (pointing at preceding Semicolon token): For-loop condition must be of int type.
	KeyFor LPar [(Lookahead(Type Name [Assign AssignmentExpression] (Comma | Semicolon)) VariableDefinition | AssignmentExpression)] Semicolon [AssignmentExpression] Semicolon [AssignmentExpression (Comma AssignmentExpression)*] RPar
	(
		StatementBlock //has scope of loop
	|	WeakerStatement //has scope of loop
	)
}

ReturnStatement = {
	//when a value is returned, there is an implicit int/long/float/double cast if needed
	//error message if there is no return value present but the function's return type is not void (pointing at KeyReturn token): A non-void function must return a value.
	//error message if there is a return value present but the function's return type is void, or this is in a static block (pointing at KeyReturn token): A void function or static block must not return a value.
	//error message if there is a return value present but its type can't be assigned to the function's return type (pointing at KeyReturn token): The type of the expression returned must be assignable to the return type of the enclosing function.
	KeyReturn [AssignmentExpression] Semicolon
}

InterruptStatement = {
	//the custom interrupt handler is called with information about the arguments
	//no argument is cloned specially for this, all are passed as if they served reference parameters
	//error message if any argument is of void type (pointing at preceding LPar or Comma token): Interrupt argument expression must be of non-void type.
	KeyInterrupt LPar [AssignmentExpression (Comma AssignmentExpression)*] RPar Semicolon
}

//
// expressions
//
AssignmentExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//the left side must be mutable
	//assignable are variables of all types, and array and tuple elements, but not vec3 components
	//evaluated from left to right
	//if there is a chain of assignments, the recursive structure causes the rightmost one to be evaluated first
	//the result of this expression is the left type, made immutable, with the reference flag set to false
	//error message if the result of the ConditionalExpression was not mutable (pointing at the operator token): Left side of assignment must be mutable.
	//error message for invalid type combination (pointing at the operator token): Invalid operand type combination.
	ConditionalExpression
	[
		(
			Assign
			//for all assignments, implicit int/long/float/double casts are available
			// all further typing must be compatible with that of the original rules, where the left type is fixed
			// combinations where the left type can not hold the result in an original rule are not permitted here
			// combinations where the left type would be implicitly casted in the original rule are not permitted here either
		|	AssignPower
		|	AssignShiftL
		|	AssignShiftR
		|	AssignAt //sets a new target location for a reference; for it, the left side must be of a type with the reference flag set, the right side must be of a matching type, with implicit int/long/float/double casts available; a variable or member on the right side can be pointed to (in general, by any reference uses) if it stands there alone as a variable or member access or as the result of a function call yielding a reference, or a combination of those, but nothing else
		|	AssignAdd
		|	AssignDiv
		|	AssignIntAnd
		|	AssignIntOr
		|	AssignIntXOR
		|	AssignMod
		|	AssignMul
		|	AssignSub
		)
		AssignmentExpression
	]
}

ConditionalExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//that BoolOrExpression serves as the condition and must be of int type; for clarity in code, implicit casting from long/float/double is not used here
	//if the condition is true, the first assignment is evaluated, else the second assignment is evaluated
	//the both AssignmentExpressions must be of matching types and not void, implicit casting is not used
	//the result of this expression is the left AssignmentExpression's type, made immutable, with the reference flag set to false
	//error message if the condition is not of int type (pointing at QuestionMark token): Conditional expression condition must be of int type.
	//error message for void or not matching AssignmentExpression types (pointing at Colon token): Invalid operand type combination.
	BoolOrExpression [QuestionMark AssignmentExpression Colon AssignmentExpression]
}

BoolOrExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of longs/floats/doubles to ints applies
	//after implicit casting, both operands must be ints
	//evaluated from left to right, if an expression evaluates to true, the following expressions are not evaluated anymore and the whole result is true (short-circuiting functionality)
	//the result is an immutable int, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	BoolXORExpression (DoublePipe BoolXORExpression)*
}

BoolXORExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of longs/floats/doubles to ints applies
	//after implicit casting, both operands must be ints
	//evaluated from left to right
	//the result is an immutable int, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	BoolAndExpression (DoubleCaret BoolAndExpression)*
}

BoolAndExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of longs/floats/doubles to ints applies
	//after implicit casting, both operands must be ints
	//evaluated from left to right, if an expression evaluates to false, the following expressions are not evaluated anymore and the whole result is false (short-circuiting functionality)
	//the result is an immutable int, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	OrExpression (DoubleAnd OrExpression)*
}

OrExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of floats to ints or longs, doubles to longs applies, so that the longer bit length is preserved
	//after implicit casting, both operands must be ints of matching bit length
	//evaluated from left to right
	//the result is an immutable int or long, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	XORExpression (Pipe XORExpression)*
}

XORExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of floats to ints or longs, doubles to longs applies, so that the longer bit length is preserved
	//after implicit casting, both operands must be ints of matching bit length
	//evaluated from left to right
	//the result is an immutable int or long, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	AndExpression (Caret AndExpression)*
}

AndExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of floats to ints or longs, doubles to longs applies, so that the longer bit length is preserved
	//after implicit casting, both operands must be ints of matching bit length
	//evaluated from left to right
	//the result is an immutable int or long, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	EqualityExpression (And EqualityExpression)*
}

EqualityExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting to the highest operand type and bit length applies when operand types are mixed: int -> float or long -> double
	//this type of expression can be used with arrays, too
	//after implicit casting, both operands must have matching types (this explicitly includes tuples and arrays)
	//evaluated from left to right
	//the result is an immutable int, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	RelationalExpression ((Equals | NotEquals) RelationalExpression)*
}

RelationalExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting to the highest operand type and bit length applies when operand types are mixed: int -> float or long -> double
	//after implicit casting, both operands must be ints, longs, floats or doubles, and must not be mixed
	//evaluated from left to right
	//the result is an immutable int, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	ShiftExpression ((Less | Greater | LessOrEqual | GreaterOrEqual) ShiftExpression)*
}

ShiftExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of floats to ints, doubles to longs applies on the left side, floats/doubles to ints on the right side
	//after implicit casting, the left operand must be an int or long, the right operand an int
	//evaluated from left to right
	//the result is an immutable int or long, with the reference flag set to false
	//the right shift is unsigned, meaning that the left bit is not duplicated, but zeroed
	//the result is undefined if the right side is negative or >= 32 (or >= 64, respectively)
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	AdditiveExpression ((ShiftL | ShiftR) AdditiveExpression)*
}

AdditiveExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting to the highest operand type and bit length applies when operand types are mixed: int -> float or long -> double
	//after implicit casting, both operands can be ints, longs, floats, doubles or vec3s, and must not be mixed
	//if the operator is Plus, implicit int/long/float/double casting also applies when there is an int, long, float or double to be added to an int, long, float or double array
	//if the operator is Plus, both operands may be arrays of strictly matching type, or the left type may be an array and the right expression be of its element type, with any combination of reference flags allowed
	//evaluated from left to right
	//the result of this expression is the left type (after casting), made immutable, with the reference flag set to false
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	MultiplicativeExpression ((Plus | Minus) MultiplicativeExpression)*
}

MultiplicativeExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting to the highest operand type and bit length applies when operand types are mixed: int -> float or long -> double
	//after implicit casting, both operands can be ints, longs, floats, doubles, and must not be mixed
	//if the operator is an Asterisk or a Slash, the left operand may be a vec3 and the right one a float, or an int, long or double that is implicitly casted to float
	//if the operator is an Asterisk, one operand may be an int and the other one an array, with longs, floats and doubles being implicitly casted to int; or the left operand may be a float and the right one a vec3, in which case an int, long or double is implicitly casted to float on the left side; or both operands may be vec3s
	//evaluated from left to right
	//after implicit casting and evaluation, the following listing yields the immutable result type, with the reference flag set to false:
	// int    x int    -> int
	// long   x long   -> long
	// float  x float  -> float
	// double x double -> double
	// float  x vec3   -> vec3
	// vec3   x float  -> vec3
	// vec3   x vec3   -> float (dot product)
	// int    x array  -> array
	// array  x int    -> array
	//error message for invalid type combination (pointing at the last operator token): Invalid operand type combination.
	UnaryExpression ((Asterisk | Slash | Percent) UnaryExpression)*
}

UnaryExpression = {
	//if the PowerExpression branch is used, the semantics of that apply, but if another branch is used:
	//if the operator is Exclamation, the operand must be int and may be implicitly casted from long/float/double to int
	//if the operator is Tilde, the operand must be int or long and may be implicitly casted from float to int, double to long
	//if the operator is Plus or Minus, the operand must be int, long, float, double or vec3
	//if there is a chain of operators in front, the recursive structure causes the rightmost operation to be evaluated first
	//the result of this expression is the operand's type, made immutable, with the reference flag set to false
	//error message for invalid type combination (pointing at the operator token): Invalid operand type combination.
	(Exclamation | Tilde | Plus) UnaryExpression
|	Minus (IntLiteral | UnaryExpression) //this direct composition allows for noting down all valid negative int and long values directly, as the most negative one without its minus is otherwise greater than the greatest allowed valid one
|	PowerExpression
}

PowerExpression = {
	//if the optional part is omitted, the semantics of the first expression child apply, but if it is present:
	//implicit casting of ints to floats or doubles, longs to doubles applies, so that the longer bit length is preserved
	//after implicit casting, both operands must be floats or doubles, and must not be mixed
	//if there is a chain of power operations, the recursive structure causes the rightmost one to be evaluated first
	//the result is an immutable float or double, with the reference flag set to false
	//error message for invalid type combination (pointing at the operator token): Invalid operand type combination.
	IncDecExpression [Power PowerExpression]
}

IncDecExpression = {
	//the result is of the type of the chosen branch
	Lookahead(DoublePlus | DoubleMinus) PreIncDecExpression
|	Lookahead(BaseExpression (DoublePlus | DoubleMinus)) PostIncDecExpression
|	BaseExpression
}

PreIncDecExpression = {
	//the result of the BaseExpression must be a mutable int or long
	//the result is an immutable int or long, with the reference flag set to false
	//error message if the expression is not of mutable int or long type (pointing at the operator token): Pre inc/dec expression must be of mutable int or long type.
	(DoublePlus | DoubleMinus) BaseExpression
}

PostIncDecExpression = {
	//the result of the BaseExpression must be a mutable int or long
	//the result is an immutable int or long, with the reference flag set to false
	//error message if the expression is not of mutable int or long type (pointing at the operator token): Post inc/dec expression must be of mutable int or long type.
	BaseExpression (DoublePlus | DoubleMinus)
}

BaseExpression = {
	//for infix operations and the like, this rule is used with a request to clone values instead of returning references to any variables, to preserve the original variable values that are otherwise used directly to save memory
	//for the case that a value is requested to be cloned in one of the following branches, the reference flag is set to false everywhere unless specified otherwise
	//the following descriptions all assume that a value is not requested to be cloned, so if a value is requested to be cloned, setting the reference flag to false is basically an override
	(
		//for all branches but QName, the resulting type does not have the reference flag set
		//for the QName branch, the resulting type inherits the reference flag from the referenced symbol
		Lookahead(Type LBrace) Array //immutable array
	|	Lookahead(KeyTuple | LPar (And | AssignmentExpression Comma)) Tuple //immutable tuple
	|	LPar AssignmentExpression RPar //expression's type, made immutable; error message for void type (pointing at LPar token): The expression in parenthesis must be of non-void type.
	|	KeyFalse      //immutable int 0
	|	KeyTrue       //immutable int 1
	|	IntLiteral    //immutable int
	|	LongLiteral   //immutable long
	|	FloatLiteral  //immutable float
	|	DoubleLiteral //immutable double
	|	CharLiteral   //immutable int
	|	StringLiteral //immutable int array
	|	(KeyChar | KeyInt) LPar AssignmentExpression RPar //explicit cast, input must be of long, float or double type, or int (in which case, instead of casting, simply nothing happens), yields immutable int; error message if argument was not an int, long, float or double (pointing at LPar token): Int conversion argument must be of int, long, float or double type.
	|	KeyLong            LPar AssignmentExpression RPar //explicit cast, input must be of int, float or double type, or long (in which case, instead of casting, simply nothing happens), yields immutable long; error message if argument was not an int, long, float or double (pointing at LPar token): Long conversion argument must be of int, long, float or double type.
	|	KeyFloat           LPar AssignmentExpression RPar //explicit cast, input must be of int, long or double type, or float (in which case, instead of casting, simply nothing happens), yields immutable float; error message if argument was not an int, long, float or double (pointing at LPar token): Float conversion argument must be of int, long, float or double type.
	|	KeyDouble          LPar AssignmentExpression RPar //explicit cast, input must be of int, long or float type, or double (in which case, instead of casting, simply nothing happens), yields immutable double; error message if argument was not an int, long, float or double (pointing at LPar token): Double conversion argument must be of int, long, float or double type.
	|	KeyVec3            LPar AssignmentExpression Comma AssignmentExpression Comma AssignmentExpression RPar //conversion, inputs must be of float type, ints, longs and doubles are implicitly casted to floats, yields immutable vec3; error message if an argument was not a float after casting (pointing at preceding LPar or Comma token): vec3 conversion argument must be of int, long, float or double type.
	|	Reinterpretation //immutable int, long, float or double
	|	Lookahead(QName LPar) FunctionCall //function's return type, made immutable
	|	Lookahead(FunctionObject) FunctionObject //immutable function type
	|	QName //variable or function parameter, yields mutable type (with the reference flag inherited from the variable's definition); error if the symbol doesn't denote a variable or function parameter (pointing at last Name token in QName): Symbol "<name>" must denote a variable in this context.
	)
	(
		Period
		(
			Lookahead(Name LPar) ObjectFunctionCall //system object function call for certain object types
		|	Name //vec3 component access (.x, .y, .z) and named tuple element access; yields a mutable type if the previous type was a mutable vec3 or tuple type, with the reference flag set according to the tuple element definitions and to false for vec3 elements; error message if the operand was not a vec3 or tuple (pointing at Name token): Named element access requires a vec3 or tuple operand.; error message if the operand is a vec3 but the name doesn't denote a vec3 component (pointing at Name token): "<name>" is not a vec3 component name.; error message if the operand is a tuple and the name doesn't denote an element in it (pointing at Name token): Element "<name>" not found in tuple.
		|	IntLiteral //anonymous tuple element access (.#), yields mutable type if the previous type was mutable; error message if the operand is not of tuple type (pointing at IntLiteral token): Indexed tuple element access requires a tuple operand.; error message if the index is out of range for the tuple (pointing at IntLiteral token): Index out of tuple element bounds (size: <count of tuple elements>, index: <supplied index>).
		)
	|	LBracket AssignmentExpression RBracket //array index, AssignmentExpression must be of int type, longs/floats/doubles are implicitly casted to ints; yields mutable type if the previous type was mutable, with the reference flag of the array element type; error message if operand is not an array (pointing at LBracket token): Array element access requires an array operand.; error message if the AssignmentExpression is not of an int type after casting (pointing at LBracket token): Array element index must be of int type.
	|	FunctionCallArguments //for calling function objects; error message if the operand is not of FunctionObject type (pointing at LPar token in FunctionCallArguments): Function call arguments require a function object operand.
	)*
}

Tuple = {
	//the element types of the tuple are those of the assignments, they must not be void
	//evaluated from left to right
	//the result is an immutable tuple, with the reference flag set to false
	//And present => reference flag set for that element's type
	//error message for void type (pointing at preceding non-And-LPar or Comma token): Tuple element expression must be of non-void type.
	KeyTuple LPar (And LPar AssignmentExpression RPar | AssignmentExpression) (Comma (And LPar AssignmentExpression RPar | AssignmentExpression))* RPar
|	LPar (And LPar AssignmentExpression RPar | AssignmentExpression) (Comma (And LPar AssignmentExpression RPar | AssignmentExpression))+ RPar
}

Array = {
	//if the type is int, long, float or double, there is an implicit int/long/float/double cast
	//after implicit casting, the element types must be equal to the type specified
	//evaluated from left to right
	//the result is an immutable array, with the reference flag set to false
	//error message for non-array type (pointing at LBrace token): Array type required.
	//error message for element type that is not assignable to the specified array type's element type (pointing at preceding LBrace or Comma token): Array element type must be assignable to the specified array type's element type.
	Type LBrace [AssignmentExpression (Comma AssignmentExpression)*] RBrace
}

Reinterpretation = {
	//a float expression can be reinterpreted as int and vice-versa
	//a double expression can be reinterpreted as long and vice-versa
	//it is not allowed to reinterpert an int as an int, a long as a long, a float as a float, or a double as a double, because the implicit cast mechanism would be hard to comprehend here (interpret<int>(int data) => interpret<int>(float data)), and interpretations for the same source and target types type are not helpful
	//the result is an immutable int, long, float or double, with the reference flag set to false
	//error message if KeyChar or KeyInt is present and the type of the AssignmentExpression is not float (pointing at LPar token): Expression to be re-interpreted as int must be of float type.
	//error message if KeyFloat is present and the type of the AssignmentExpression is not int (pointing at LPar token): Expression to be re-interpreted as float must be of int type.
	//error message if KeyLong is present and the type of the AssignmentExpression is not double (pointing at LPar token): Expression to be re-interpreted as long must be of double type.
	//error message if KeyDouble is present and the type of the AssignmentExpression is not long (pointing at LPar token): Expression to be re-interpreted as double must be of long type.
	KeyInterpret Less (KeyChar | KeyInt | KeyLong | KeyFloat | KeyDouble) Greater LPar AssignmentExpression RPar
}

FunctionCall = {
	//works only on functions, use (x)(args) for function objects
	//the result of this expression is the function's return type, made immutable, with inherited reference flag
	//predefined system functions:
	// double abs              (double                                 );
	// float  abs              (float                                  );
	// int    abs              (int                                    );
	// long   abs              (long                                   );
	// double acos             (double                                 );
	// float  acos             (float                                  );
	// double asin             (double                                 );
	// float  asin             (float                                  );
	// double atan             (double                                 );
	// float  atan             (float                                  );
	// double atan             (double y   , double x                  );
	// float  atan             (float  y   , float  x                  );
	// double clamp            (double x   , double vMin  , double vMax);
	// float  clamp            (float  x   , float  vMin  , float  vMax);
	// int    clamp            (int    x   , int    vMin  , int    vMax);
	// long   clamp            (long   x   , long   vMin  , long   vMax);
	// float  constE           (                                       );
	// float  constPi          (                                       );
	// int    convSuccess      (                                       ); //true if toInt/toLong/toFloat/toDouble succeeded, otherwise false
	// double cos              (double                                 );
	// float  cos              (float                                  );
	// vec3   dirVector        (float  h   , float  p                  );
	// double div              (double x   , double y                  ); //floor(x/y);
	// floor  div              (floor  x   , floor  y                  ); //~
	// int    div              (int    x   , int    y                  ); //~
	// long   div              (long   x   , long   y                  ); //~
	// void   error            (string                                 ); //terminates program execution with an error message, intended to be used for debugging
	// double floor            (double                                 );
	// float  floor            (float                                  );
	// float  frand            (                                       ); //0 <= result < 1
	// double log              (double x   , double base               );
	// float  log              (float  x   , float  base               );
	// double max              (double x   , double y                  );
	// float  max              (float  x   , float  y                  );
	// int    max              (int    x   , int    y                  );
	// long   max              (long   x   , long   y                  );
	// double min              (double x   , double y                  );
	// float  min              (float  x   , float  y                  );
	// int    min              (int    x   , int    y                  );
	// long   min              (long   x   , long   y                  );
	// double pmod             (double x   , double y                  ); //x-y*floor(x/y)
	// float  pmod             (float  x   , float  y                  ); //~
	// int    pmod             (int    x   , int    y                  ); //~
	// long   pmod             (long   x   , long   y                  ); //~
	// double pow              (double x   , double exp                ); //same as x**exp
	// float  pow              (float  x   , float  exp                ); //~
	// double root             (double x   , double degree             );
	// float  root             (float  x   , float  degree             );
	// double rotDelta         (double from, double to                 ); //0 <= {from, to} < 360, yields smallest angle offset - see appendix A
	// float  rotDelta         (float  from, float  to                 ); //~
	// int    sign             (double                                 );
	// int    sign             (float                                  );
	// int    sign             (int                                    );
	// int    sign             (long                                   );
	// double sin              (double                                 );
	// float  sin              (float                                  );
	// int[]  sort             (int    []                              ); //fast heap sort (Not stable!), returns indexes
	// int[]  sort             (long   []                              ); //~
	// int[]  sort             (float  []                              ); //~
	// int[]  sort             (double []                              ); //~
	// int[]  sortStable       (int    []                              ); //slower stable sorting using binary search, returns indexes
	// int[]  sortStable       (long   []                              ); //~
	// int[]  sortStable       (float  []                              ); //~
	// int[]  sortStable       (double []                              ); //~
	// int[]  sortStrings      (string&[]                              ); //the sorting order is that of the Microsoft .Net platform
	// int[]  sortStringsStable(string&[]                              ); //~
	// double sqr              (double                                 );
	// float  sqr              (float                                  );
	// double sqrt             (double                                 );
	// float  sqrt             (float                                  );
	// void   srand            (int                                    );
	// double tan              (double                                 );
	// float  tan              (float                                  );
	// double toDouble         (string                                 ); //returns 0 on error; see also: convSuccess
	// float  toFloat          (string                                 ); //~
	// int    toInt            (string                                 ); //~
	// long   toLong           (string                                 ); //~
	// string toString         (double                                 );
	// string toString         (float                                  );
	// string toString         (int                                    );
	// string toString         (long                                   );
	//if no function is found, it is first attempted to find a fitting function object with the same QName
	//error message if the retrieved function group has no suitable overload (pointing at LPar token in FunctionCallArguments): No suitable overload of function "<name>" found that can be called with these arguments.
	QName FunctionCallArguments
}

ObjectFunctionCall = {
	//depending on the object type this is called on, the following functions are available:
	//vec3 type:
	// vec3  crossP(vec3)
	// float length()
	// float squaredLength()
	// void  negate(): type must be mutable
	// void  rotate(float h, float p, float b): type must be mutable
	// void  rotateH(float h): type must be mutable
	// void  rotateP(float p): type must be mutable
	// void  rotateB(float b): type must be mutable
	// void  rotateDegOrtho(float h, float p, float b): type must be mutable
	// void  rotateDegHOrtho(float h): type must be mutable
	// void  rotateDegPOrtho(float p): type must be mutable
	// void  rotateDegBOrtho(float b): type must be mutable
	// float heading()
	// float pitch()
	// float angle(vec3 other, float solverEpsilon) - see appendix A
	//T[] type:
	// void capacity(int capacity): type must be mutable
	// void clear(): type must be mutable
	// void erase(int index): type must be mutable
	// void erase(int index, int length): type must be mutable
	// int  equals(int index, T[] other, int otherIndex, int length): result is true or false
	// int  indexOf(T elem)
	// int  indexOf(T elem, int start)
	// int  indexOf(T[] subrange)
	// int  indexOf(T[] subrange, int start)
	// void insert(int index, T): type must be mutable
	// void insert(int index, T[]): type must be mutable
	// int  lastIndexOf(T elem)
	// int  lastIndexOf(T elem, int end)
	// int  lastIndexOf(T[] subrange)
	// int  lastIndexOf(T[] subrange, int end)
	// int  length()
	// T[]  range(int index, int length)
	//all results are immutable, with the reference flag set to false
	//error message if anything is wrong (pointing at Name token): The type of the object this system function "<name>" is called on does not fit, or the function or overload for these arguments was not found.
	Name FunctionCallArguments
}

FunctionCallArguments = {
	//the types are checked against the set of selected functions, and if they match one of them fully, that match is used
	//if a reference parameter (&) is initialized without a variable, reassigning it will simply have no side-effects
	//if the function object to be used has been specified uniquely, the supplied arguments are checked against the expected parameters, allowing for the following error messages
	// error message if there is an expectation for the parameters and the arguments supplied differ in count (pointing at LPar token): Count of arguments doesn't match function parameter count.
	// error message if there is an expectation for the parameters and an argument supplied differs in typing, or if an argument is of void type (pointing at preceding LPar or Comma token): Function call argument type must be assignable to function parameter type.
	LPar [AssignmentExpression (Comma AssignmentExpression)*] RPar
}

FunctionObject = {
	//this is a fully qualified unambiguous function object, which can be assigned to function pointers of the matching type
	//note that although I named this a "function object", it doesn't qualify as a function object as known in the literature, as it can't maintain an internal state
	//yields the function
	//error message if the specified function group doesn't have an overload with the specified parameter list (pointing at last Name token in QName): No suitable overload of function "<name>" with given parameter list found.
	QName FunctionObjectParameters
}

FunctionObjectParameters = {
	//names are ignored
	//And tokens to distinguish reference parameters are omitted here because there can't be overloads which differ only in the reference flags for parameters - without them, we can unambiguously specify each function already
	Less [FunctionObjectParameter (Comma FunctionObjectParameter)*] Greater
}

FunctionObjectParameter = {
	//name is ignored
	Type [Name]
}

//for lookups in scopes:
// error message if there is an ambiguity between symbols of the used namespaces of the current scope during the alternative lookup (pointing at last Name token in QName): Found multiple fitting symbols in the used namespaces while searching for symbol "<name>".
// error message if there is an ambiguity because multiple function overloads of the used namespaces of the current scope during the alternative lookup matched given arguments (pointing at last Name token in QName): Found multiple fitting function overloads in the used namespaces while searching for function "<name>".
// error message if there is an ambiguity between child namespaces of the used namespaces of the current scope during the alternative lookup (pointing at last Name token in QName): Found multiple fitting child namespaces in the used namespaces while searching for namespace "<name>".
//after all compilation units have been processed:
// error message if a function was used as a function object or called directly but was never defined (pointing at last Name token in QName at first occurrence of the call): Use of or call to declared but undefined function "<name>".
// error message if a function object pointing at nothing was called: Dynamic call to nothing.
//during runtime:
// References in this language can not be circular, this emerges from the syntax rules of the grammar and is prerequisite for the following discussion, decision and specification. If something is deallocated and then accessed through a reference, the old data is to be used. In a runtime environment with native garbage collection, this is the default behavior. In a runtime environment without native garbage collection, garbage collection must be simulated for objects to be deallocted. A runtime implementation is free to allocate and deallocate variables at the entry and exit of functions instead of the scopes that they are valid in (e.g. within loops), to optimize for performance. If an error check was to be conducted for objects used after deallocation, this check would have to happen when deallocating variables according to their scope. Because this would likely incur a performance and compiling complexity penalty for programs without any such errors, implementations on runtime environments without native garbage collection are to emulate garbage collection for deallocated but still referenced variables.

//Appendix A
//
//vec3.angle(vec3)
//	a*b = cos(ang(a, b))*|a|*|b|
//	acos(a*b/(|a|*|b|)) = ang(a, b) => and then clamp argument of acos to [-1, 1] because of finite precision during calculations
//
//rotDelta(float from, float to)
//	|T-------|T-------|T-------| //to on the negative side (right)
//	|-----T--|-----T--|-----T--| //to on the positive side (left )
//	|--------|--F-----|--------| //from
//	-360     0        360      720
//	right: T <= F ? F-T : F-(T-360)
//	left : F <= T ? T-F : T+360-F
//	choose smaller value, return positive value for left, or negative value for right
